
/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
 *	CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
 *	AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
 *	ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
 *	OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
 *	WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
 *	WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
 *	DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 *	FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
 *	DrawingView.m
 *
 *	This view represents the page that the image is drawn onto.
 *
 *	An offscreen buffer, bufferId, is used to draw into and then
 *	this buffer is composited onscreen. This technique allows for the
 *	separation of the static drawing from the temporal drawing (modal
 *	loop redrawing and control point display).  The static drawing takes
 *	place in the buffer while the temporal drawing takes place in this view.
 *	The static drawing is stuff that is complex and that will stay around for
 *	a while. Saving this drawing in a buffer elimates having to redraw it
 *	for something simple like drawing a control point or something.
 *	(The bufferId is the content view of a plain window the size of this view);
 *
 *	Version:	2.0
 *	Author:	Ken Fromm
 *	History:
 *			03-17-91		Added this comment, fixed the preview exporting
 *						section.
 */

#import "DrawingView.h"
#import "Document.h"
#import "GraphicImport.h"
#import "SaveAsPanel.h"
#import "epsf.h"

#import "DrawingViewWraps.h"

#import <appkit/Cell.h>
#import <appkit/ClipView.h>
#import <appkit/Matrix.h>
#import <appkit/NXCursor.h>
#import <appkit/NXImage.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/Text.h>
#import <appkit/nextstd.h>

#import <dpsclient/dpsclient.h>
#import <dpsclient/wraps.h>
#import <math.h>

extern char	ControlFont[ ];

extern const NXRect  DefaultWindowRect;

static char	EpsfProcSet[] = "EPSF_Illustrator_abbrev 0 0";

void compositeBuffer(int gstate, const NXRect *srce, const NXPoint *dest, int op)
{
  	PScomposite(NX_X(srce), NX_Y(srce), NX_WIDTH(srce), NX_HEIGHT(srce),
                gstate, dest->x, dest->y, op);
}

/*
*	The buffer is used to prevent unnecessary drawing by
*	retaining the image offscreen.
*
*	Create a plain window the size of the rectangle passed in and
*	then insert a view into the window as a subview. 
*/
static id createBuffer(const NXSize *size)
{
	id		buffer, window;

	NXRect	contRect;
	
	contRect.origin.x = contRect.origin.y = 0;
	contRect.size = *size;
	window = [Window newContent:&contRect
				style:NX_PLAINSTYLE
				backing:NX_RETAINED
				buttonMask:0
				defer:NO] ;

	buffer = [[[View  newFrame:&contRect] allocateGState] setClipping:NO];
	[[window  setContentView:buffer ] free];

	[ window  display];

	return buffer;
}

@implementation DrawingView

+newFrame:(const NXRect *) frameRect
{	
	self = [super  newFrame:frameRect]; 
	[[self  allocateGState] setClipping:NO]; 
	
	bufferId = createBuffer(&frameRect->size);

	return self;
}

- free
{
	[graphicId  free];
	
	return [super  free];
}

- buffer
{
	return bufferId;
}

- image
{
	return imageId;
}

- (float) controlPointSize
{
	return  FONTSIZE;
}

- resetCursorRects
{
	[self addCursorRect:&bounds  cursor:[NXApp  cursor]];

	return self;
}

/*
*	This method copies the PostScript code for the graphics and writes it to the
*	stream passed in. Includes the preview image when appropriate.
*/
- writePSToStream:(NXStream *) stream
{
	id			nximageId;

	NXRect		bbox;

	if (stream)
	{
		nximageId = NULL;
		imageId = NULL;

		[graphicId  getBounds:&bbox];
		if ([[SaveAsPanel  new]  format] == SAVE_EPSPREVIEW)
		{
			nximageId = [[NXImage  alloc]  initSize:&bbox.size];
			[nximageId  useCacheWithDepth:NX_TwoBitGrayDepth];
			if ([nximageId  lockFocus])
			{
				PStranslate(-bbox.origin.x, -bbox.origin.y);
				PSsetgray(NX_WHITE);
				NXRectFill(&bbox);
				[graphicId  drawObject:&bbox  withFlags:NOFLAGS  inView:self];

				imageId = [[NXBitmapImageRep  alloc]  initData:NULL  fromRect:&bbox];
				[nximageId  unlockFocus];
			}
		}

		[self copyPSCodeInside:&bbox to:stream];

		[nximageId  free];
		[imageId  free];
		imageId = NULL;
	}

	return self;
}

/*  Deletes the epsf object if it is selected.  */
- delete:sender
{
	float			knobsize;

	NXRect		rect;

	if ([graphicId  selected])
	{
		knobsize = -[self  controlPointSize];
		[graphicId  getBounds:&rect];
		NXInsetRect(&rect, knobsize, knobsize);
		[graphicId  free];
		graphicId = NULL;

		[self  display:&rect  :1];
	}

	return self;
}

/*
*	Constrain the point within the view. An offset is needed because when
*	an object is moved, it is often grabbed in the center of the object. If the
*	lower left offset and the upper right offset were not included then part of
*	the object could be moved off of the view. (In some applications, that might
*	be allowed but in this one the object is constrained to always lie in the
*	page.)
*/
- constrainPoint:(NXPoint *)aPt  withOffset:(const NXSize*)llOffset  :(const NXSize*)urOffset
{
	float			margin;

	NXPoint		viewMin, viewMax;

	margin = ceil(FONTSIZE/2);

	viewMin.x = bounds.origin.x + llOffset->width + margin;
	viewMin.y = bounds.origin.y + llOffset->height + margin;

	viewMax.x = bounds.origin.x + bounds.size.width - urOffset->width - margin;
	viewMax.y = bounds.origin.y + bounds.size.height  - urOffset->height - margin;

	aPt->x = MAX(viewMin.x, aPt->x);
	aPt->y = MAX(viewMin.y, aPt->y);

	aPt->x = MIN(viewMax.x, aPt->x);	
	aPt->y = MIN(viewMax.y, aPt->y);

	return self;
}

/*
*	Constrain a rectangle within the view.
*/
- constrainRect:(NXRect *)aRect
{
	float			margin;
	
	NXPoint		viewMin, viewMax;

	margin = ceil(FONTSIZE/2);

	viewMin.x = bounds.origin.x + margin;
	viewMin.y = bounds.origin.y + margin;

	viewMax.x = bounds.origin.x + bounds.size.width  - aRect->size.width - margin;
	viewMax.y = bounds.origin.y + bounds.size.height - aRect->size.height - margin;

	aRect->origin.x = MAX(viewMin.x, aRect->origin.x);
	aRect->origin.y = MAX(viewMin.y, aRect->origin.y);

	aRect->origin.x = MIN(viewMax.x, aRect->origin.x );	
	aRect->origin.y = MIN(viewMax.y, aRect->origin.y);

	return self;
}

/*
 *	Redraws the graphic. The image from the buffer is composited
 *	into the window and then the changed object is drawn atop the
 *	old image. A copy of the image is necessary because if the
 *	window were to allow scrolling the buffer would also have to be
 *	scrolled. Were this to happen the old image might have to be redrawn.
 *	As a result, a copy is created and the changes performed on the
 *	copy.  Care is taken to limit the amount of area that must be
 *	composited and redrawn.
 *
 *	Even though scrolling is not allowed in this instance, a copy is
 *	created anyways.
  */
- redrawObject:(int) pt_num
{
	id			copyId;

	BOOL		tracking,
				dirtyFlag = NO;

	int			old_mask;
	
	float			knobsize;

	NXPoint		pt, pt_last, pt_old, delta;
	
	NXRect		rect_now, rect_start, rect_last;

	NXEvent		*event;

	/*
	*	The freeTemp method is messaged at the bottom so as not to free
	*	the shared items. 
	*/		
	copyId = [graphicId  copy];

	knobsize = -[self  controlPointSize];
	[copyId  getBounds:&rect_start];
	NXInsetRect(&rect_start, knobsize, knobsize);

	rect_now = rect_last = rect_start;

	[copyId  getPoint:pt_num :&pt_last];
	pt_old = pt_last;

	old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
	event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
	if (event->type != NX_MOUSEUP)
	{
		tracking = YES;
		while (tracking)
		{
			pt = pt_old = event->location;

			[self convertPoint:&pt  fromView:nil];
			[copyId constrainPoint:&pt  forPtNum:&pt_num
					inRect:&bounds  withFlags:event->flags];

			delta.x = pt.x - pt_last.x;
			delta.y = pt.y - pt_last.y;

			if (delta.x || delta.y)
			{
				dirtyFlag = YES;
				/* Change the point location and get the new bounds. */
				[copyId setPoint:pt_num :&delta];
				[copyId  getBounds:&rect_now];
				NXInsetRect(&rect_now, knobsize, knobsize);

				/* Composite the old image and then redraw the new one. */
				compositeBuffer([bufferId  gState], &rect_last, &rect_last.origin, NX_COPY);
				[self  drawObject:copyId  forRect:&rect_now  withFlags:REDRAWFLAG];
				[self  drawControl:copyId  forRect:&rect_now  withFlags:NOFLAGS];

				/* Sync up the drawing so it proceeds a little smoother. */
				[window flushWindow];
				NXPing();
				
				rect_last = rect_now;
				pt_last = pt;
			}

			event = [NXApp getNextEvent:NX_MOUSEUPMASK|
							NX_MOUSEDRAGGEDMASK];
			tracking = (event->type != NX_MOUSEUP);
		}
	}
	[window setEventMask:old_mask];

	[graphicId  freeTemp];
	graphicId = copyId;

	if (dirtyFlag)
	{
		/*
		*  The view has already been focused and we know what
		*  has to be redrawn so call drawSelf:: instead of display
		*/
		NXUnionRect(&rect_last, &rect_start);
		[self  drawSelf:&rect_start :1];
		[window flushWindow];
		NXPing();
	}

	return self;
}

/*
 *	Moves the selected objects by performing a translate before drawing
 *	the objects. This approach is used because the objects are drawn
 *	into windows and drawing simply means compositing the windows.
 *
 *	The offsets constrain the selected object to stay within the dimensions
 *	of the view. 
 */
- moveObject:(NXEvent *)event
{
	BOOL		tracking,
				dirtyFlag = NO;
	
	int			old_mask;

	float			knobsize;

	NXSize		llOffset, urOffset;

	NXPoint		pt, pt_last, pt_old, delta;

	NXRect		rect_now, rect_start, rect_last;

	knobsize = -[self  controlPointSize];
	[graphicId  getBounds:&rect_start];
	NXInsetRect(&rect_start, knobsize, knobsize);
	rect_now = rect_last = rect_start;

	pt_last = pt_old = event->location;
	[self  convertPoint:&pt_last  fromView:nil];
	
	/* Calculate where the mouse point falls relative to the object. */
	llOffset.width = pt_last.x - rect_start.origin.x;
	llOffset.height = pt_last.y - rect_start.origin.y;	
	urOffset.width = rect_start.origin.x + rect_start.size.width - pt_last.x;
	urOffset.height = rect_start.origin.y + rect_start.size.height - pt_last.y;

	/* Return nil if the the mouse was not dragged. */
	old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
	event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
	if (event->type != NX_MOUSEUP)
	{
		tracking = YES;
		while (tracking)
		{			
			pt = pt_old = event->location;
				
			[self  convertPoint:&pt fromView:nil];
			[self  constrainPoint:&pt  withOffset:&llOffset :&urOffset];
			[self  constrainPoint:&pt_last  withOffset:&llOffset :&urOffset];
			delta.x = pt.x - pt_last.x;
			delta.y = pt.y - pt_last.y;

			if (delta.x || delta.y)
			{
				dirtyFlag = YES;
				NXOffsetRect(&rect_now, delta.x, delta.y);
				[self  constrainRect:&rect_now];

				/*
				*  Composite the old image into the window and then
				*  translate the user space and draw the graphicId object.
				*/
				compositeBuffer([bufferId  gState], &rect_last, &rect_last.origin, NX_COPY);
				PSgsave();
					PStranslate(rect_now.origin.x - rect_start.origin.x,
						rect_now.origin.y - rect_start.origin.y);
					[self  drawObject:graphicId  forRect:&rect_start  withFlags:MOVEFLAG];
					[self  drawControl:graphicId  forRect:&rect_start 
							withFlags:NOFLAGS];
				PSgrestore();

				[window flushWindow];
				NXPing();
				
				rect_last = rect_now;
				pt_last = pt;
			}
			event = [NXApp getNextEvent:NX_MOUSEUPMASK|
						NX_MOUSEDRAGGEDMASK];

			tracking = (event->type != NX_MOUSEUP);
		}

		if (dirtyFlag)
		{
			delta.x = rect_now.origin.x - rect_start.origin.x;
			delta.y = rect_now.origin.y - rect_start.origin.y;			[graphicId  moveAll:&delta];
	
			/*
			*  The view has already been focused and we know what
			*  has to be redrawn so call drawSelf:: instead of display
			*/
			NXUnionRect(&rect_now, &rect_start);
			[self drawSelf:&rect_start :1];
			[window flushWindow];
			NXPing();
		}
	}
	[window setEventMask:old_mask];

	return self;
}

/* Check to see whether a control point has been hit. */
 - checkControl:(const NXPoint *) p :(int *) pt_num
 {
	NXRect	hitRect;

	NXSetRect(&hitRect, p->x - HITSETTING/2, p->y - HITSETTING/2, HITSETTING, HITSETTING);

	return [graphicId  hitControl:&hitRect :pt_num  forSize:[self  controlPointSize]];
 }

/* 
*	Check to see whether the epsf object has been hit. Return it if yes.
*/
- checkObject:(const NXPoint *) p
 {
	UPath		*hitPoint;

	/*  Bounding Box */
	hitPoint = [NXApp  hitPoint];
	hitPoint->pts[0] = floor(p->x - HITSETTING/2);
	hitPoint->pts[1] = floor(p->y - HITSETTING/2);
	hitPoint->pts[2] = ceil(p->x + HITSETTING/2);
	hitPoint->pts[3] = ceil(p->y + HITSETTING/2);
	
	/*  Moveto */
	hitPoint->pts[4] = p->x - HITSETTING/2;
	hitPoint->pts[5] = p->y - HITSETTING/2;

	/* Rlineto's */
	hitPoint->pts[7] = HITSETTING;
	hitPoint->pts[8] = HITSETTING;
	hitPoint->pts[11] = -HITSETTING;

	return [graphicId  hitObject:hitPoint];
 }
 
 /*
 *	Set the object as selected then draw the control points.
 */
 - selectObject
 {
 	float			knobsize;

	NXRect		drawRect;

	[graphicId  setSelected:YES];

	knobsize = -[self  controlPointSize];
	[graphicId  getBounds:&drawRect];
	NXInsetRect(&drawRect, knobsize, knobsize);

	[self  drawControl:graphicId  forRect:&drawRect  withFlags:NOFLAGS];
	[window  flushWindow];

	return self;
}

/*
*	Redraw thecontrol points for the occupied portion. The control points
*	are drawn in this view since they can be drawn quickly.
*/
- deselectObject
{
	float			knobsize;

	NXRect		drawRect;

	[graphicId  setSelected:NO];

	knobsize = -[self  controlPointSize];
	[graphicId  getBounds:&drawRect];
	NXInsetRect(&drawRect, knobsize, knobsize);

	[self  drawControl:graphicId  forRect:&drawRect  withFlags:CLEARFLAG];
	[window  flushWindow];
	
	return self;
}

/*
*	Test for a mouse down hit on either the control points or the object.
*/ 
- testObject:(NXEvent *)event
{
	int			pt_num;
	
	NXPoint		p;
 
 	p = event->location; 
	[self convertPoint:&p fromView:nil];
	[self  lockFocus];
		if ([graphicId  selected])
		{
			if ([self  checkControl:&p :&pt_num])
				[self  redrawObject:pt_num];
			else if ([self  checkObject:&p])
				[self  moveObject:event];
			else
				[self  deselectObject];
		}
		else if ([self checkObject:&p])
			[self  selectObject];
	[self  unlockFocus];	

	return self;
}

/*
 *	Pass the file name to the factory TIFF or EPS object to create a new
 *	instance. If successful then free the previous epsf object.
 */
- importFile:(const char *) file
{
	id		tempId;

	char		*end;

	NXStream	*stream;

	if (file)
	{
		end = strrchr(file, '.');
		if (end)
		{
			stream = NXMapFile(file, NX_READONLY);
			if (stream)
			{
				if (strncmp(end, ".tiff", 5) == 0 ||
					strncmp(end, ".ps", 3) == 0 ||
						strncmp(end, ".eps", 4) == 0)
				{
	 				tempId = [[GraphicImport alloc]  initFromStream:stream];
					if (tempId)
					{
						[tempId  setFilename:file];
						[self  delete:self];

						[graphicId  free];
						graphicId = tempId;
						[NXApp  setOperation:OP_IMPORT];

						return self;
					}
					NXCloseMemory(stream, NX_FREEBUFFER);
				}
				else
					Notify("Import Error", "Unable to open file.");
			}
		}
		else
			Notify("Import Error", "Unable to import file. Unrecognized file type.");
	}

	return nil;
}

/*
 *	Place the epsId with its upper left corner at p;
 */
- placeObjectAt:(const NXPoint *) p
{
	float			knobsize;

	NXPoint		pt;

	NXRect		rect_draw;

	if (graphicId)
	{
		pt = *p;

		[graphicId  getBounds:&rect_draw];
		pt.y = pt.y - rect_draw.size.height;
		[graphicId  setOrigin:&pt];

		knobsize = -[self  controlPointSize];
		[graphicId  getBounds:&rect_draw];
		NXInsetRect(&rect_draw, knobsize, knobsize);

		[self  display:&rect_draw :1];
	}

	return self;
}

/*
 *	Begins the setup for placing an imported file into the document.
 *	The object for the file is created and then waits for the mouse
 *	down to begin placement and sizing. This method first gets the
 *	object and then messages the redrawObject method to draw
 *	the subsequent sizing rectangles.
 */
- importObject:(NXEvent *)event
{
	float			knobsize;

	NXPoint		p;

	NXRect		rect_draw;
 
 	if (graphicId)
	{
		p = event->location; 
		[self convertPoint:&p fromView:nil];

		NXSetRect(&rect_draw, p.x, p.y-SIZE_MIN, SIZE_MIN, SIZE_MIN);
		[graphicId  setBounds:&rect_draw];

		knobsize = -[self  controlPointSize];
		[graphicId  getBounds:&rect_draw];
		NXInsetRect(&rect_draw, knobsize, knobsize);

		[self  lockFocus];
			[self  drawObject:graphicId  forRect:&rect_draw  withFlags:REDRAWFLAG];
			[self  drawControl:graphicId  forRect:&rect_draw  withFlags:NOFLAGS];
			[self  redrawObject:8];

			if ([graphicId  error])
			{
				[graphicId  getBounds:&rect_draw];
				NXInsetRect(&rect_draw, knobsize, knobsize);
				[graphicId  free];
				graphicId = NULL;

				[self  display:&rect_draw  :1];
			}
			else
				[self  selectObject];
		[self  unlockFocus];
	}

	return self;
}

 /*
 *	Depending on the current operation, check for selection, zoom or
 *	import a file.
 */
- mouseDown:(NXEvent *)event
{
	int		operation;

	operation = [NXApp  operation];
	switch (operation)
	{
		case  OP_SELECT:
			[self  testObject:event];
			break;
		case  OP_IMPORT:
			[self  importObject:event];
			[NXApp  setOperation:OP_SELECT];
			break;
	}

	return self;
}

/*
 *	Deletes the epsf object if selected.
 */
- keyDown:(NXEvent *) event
{
	if ([NXApp  operation] == OP_SELECT && 
	     event->data.key.charSet == NX_ASCIISET &&
	     event->data.key.charCode == NX_DELETE)
		return [self  delete:self];
	else
		return nil;
}

/*
*	Draw the control points using the user path buffer to hold
*	the data for the xyshow.
*/
- drawControl:object  forRect:(NXRect *)r  withFlags:(int)flags
{
	float			knobsize;

	NXPoint		lastpoint;
	
	NXRect		rect;

	UPath		*upathBuffer;

	if (r)
		rect = *r;
	else
		[self  getVisibleRect:&rect];

	if (flags & CLEARFLAG)
		compositeBuffer([bufferId gState], &rect, &rect.origin, NX_COPY);

	if ([object  selected])
	{
		lastpoint.x = 0;
		lastpoint.y = 0;

		upathBuffer = [NXApp upathBuffer];
		upathBuffer->num_ops = 0;
		upathBuffer->num_pts = 0;

		knobsize = [self  controlPointSize];
		NXInsetRect(&rect, -knobsize/2, -knobsize/2);
		[object  putControlPoints:upathBuffer  forRect:&rect  :&lastpoint];

		upathBuffer->ops[upathBuffer->num_ops] = 0;
		upathBuffer->pts[upathBuffer->num_pts] = 0;
		upathBuffer->pts[upathBuffer->num_pts + 1] = 0;

		if (upathBuffer->num_ops > 0)
		{
			PSWSetControlPoints(ControlFont, knobsize, NX_BLACK, 0.15); 
			PSWDrawControlPoints(upathBuffer->pts[0], upathBuffer->pts[1],
				&upathBuffer->pts[2], upathBuffer->num_pts, upathBuffer->ops);
		}
	}

	return self;
}

- drawObject:object  forRect:(NXRect *)r  withFlags:(int) flags
{
	NXRect		rect;

	if (r)
		rect = *r;
	else
		[self  getVisibleRect:&rect];

	[object  drawObject:&rect  withFlags:flags  inView:self];

	return self;
}

/*
*	Fill in the background of the rectangle and then draw the epsf object.
*	If NX_DRAWING, then draw into the buffer and composite it into
*	the window.
*/
- drawSelf:(NXRect *)r :(int) count
{
	if (NXDrawingStatus == NX_DRAWING)
	{
		[bufferId  lockFocus];
		PSsetgray(NX_WHITE);
		NXRectFill(r);
	}

	[self  drawObject:graphicId  forRect:r  withFlags:REFRESHFLAG];

	if (NXDrawingStatus == NX_DRAWING && !imageId)
	{
		[bufferId  unlockFocus];
		compositeBuffer([bufferId gState], r, &r->origin, NX_COPY);
		[self  drawControl:graphicId  forRect:r  withFlags:NOFLAGS];
	}
	
	return self;			
}

/*
*	This method is only overridden to eliminate during a copy
*	the rectclip and gsave/grestore pairing that results from
*	a lockFocus. These are usually harmless operations but
*	they interfere with trying to produce Illustrator format files.
*/
- display:(NXRect *)r  :(int) count  :(BOOL)flag
{
	if (NXDrawingStatus == NX_COPYING)
	{
		[self  drawSelf:r  :count];
		DPSFlushContext(DPSGetCurrentContext());
	}
	else
		[super  display:r  :count  :flag];

	return self;
}

- (BOOL)acceptsFirstResponder
{
	return YES;
}

/*
*	Used when printing. Returns the global resources used in the document.
*/
- addResources:(Resource *) resourceDoc
{
	NXAtom		string;

	if (NXDrawingStatus == NX_COPYING)
	{
		string = NXUniqueString(EpsfProcSet);
		if (!resourceDoc[RES_PROCSETS].states[RES_PRESENT])
			resourceDoc[RES_PROCSETS].states[RES_PRESENT] = [List  new];

		[resourceDoc[RES_PROCSETS].states[RES_PRESENT]
						addObjectIfAbsent:(id) string]; 

		if (!resourceDoc[RES_PROCSETS].states[RES_SUPPLIED])
			resourceDoc[RES_PROCSETS].states[RES_SUPPLIED] = [List  new];

		[resourceDoc[RES_PROCSETS].states[RES_SUPPLIED]
						addObjectIfAbsent:(id) string]; 
	}	

	return self;
}	

/*
*	Print the %%DocumentResource comments. A list of the resources is
*	accumulated from the epsf files (only one in this case).
*	The list is then written to the current context.
*/
- beginResourceComments:(const NXRect *) bbox
{
	int			i, j;

	Resource		resourceDoc[RES_NUMTYPES];

	bzero(&resourceDoc, sizeof(resourceDoc));
	[self  addResources:resourceDoc];
	[graphicId  addResources:resourceDoc  for:(NXRect *) bbox];

	for (i = 0; i < RES_NUMTYPES; i++)
	{
		for (j = 0; j < RES_NUMSTATES; j++)
		{
			if (resourceDoc[i].states[j])
			{
				WriteEpsfResource(resourceDoc[i].states[j], i, j);
				[resourceDoc[i].states[j]  free];
			}
		}
	}

	return self;
}

/*
*	Write out the necessary information. Overridden to include
*	the fonts from the EPSF files.
*/
- beginPrologueBBox:(const NXRect *)boundingBox 
	creationDate:(const char *)dateCreated 
	createdBy:(const char *)anApplication 
	fonts:(const char *)fontNames 
	forWhom:(const char *)user 
	pages:(int)numPages 
	title:(const char *)aTitle
{
	time_t		clock;

	DPSContext	ctxt;

	ctxt = DPSGetCurrentContext();
	if (!boundingBox)
		boundingBox = &bounds;

	if (!dateCreated)
	{
		clock = time(0);
		dateCreated = ctime(&clock);
	}

	if (!anApplication)
		anApplication = [NXApp  appName];

	if (!user)
		user = (char *) getlogin();

	if (numPages <= 0)
		numPages = 1;

	if (!aTitle)
		aTitle = [[window  delegate]  filename];

	DPSPrintf(ctxt, "%%!PS-Adobe-2.0 EPSF-1.2\n");
	DPSPrintf(ctxt, "%%%%Creator: %s\n", anApplication);
	DPSPrintf(ctxt, "%%%%For: %s\n", user);
	DPSPrintf(ctxt, "%%%%Title: %s\n", aTitle);
	DPSPrintf(ctxt, "%%%%CreationDate: %s", dateCreated);
	DPSPrintf(ctxt, "%%%%BoundingBox: %d %d %d %d\n", (int) floor(boundingBox->origin.x),
		(int) floor(boundingBox->origin.y),
		(int) ceil(boundingBox->origin.x + boundingBox->size.width),
		(int) ceil(boundingBox->origin.y + boundingBox->size.height));
	if (NXDrawingStatus == NX_COPYING)
		DPSPrintf(ctxt, "%%AI3_TemplateBox: %d %d %d %d\n", 306, 396, 306, 396);
	if (NXDrawingStatus != NX_COPYING)
		DPSPrintf(ctxt, "%%%%Pages: %d\n", numPages);

	[self  beginResourceComments:boundingBox];

	return self;
}

/*
*	Includes the abbreviated Illustrator proc set so that
*	the eps files produced through Save To will print on their
*	own. Also includes the preview data as a comment when
*	specified.
*/
- endHeaderComments
{
	DPSContext	ctxt;

	if (NXDrawingStatus == NX_COPYING)
	{
		ctxt = DPSGetCurrentContext();
		DPSPrintf(ctxt, "%%%%EndComments\n\n");

		DPSPrintf(ctxt, "%%%%BeginProcSet: EPSF_Illustrator_abbrev 0 0\n");
		WriteEpsfProcSetDef ();
		DPSPrintf(ctxt, "%%%%EndProcSet\n\n");

		if (imageId && [[SaveAsPanel  new]  format] == SAVE_EPSPREVIEW)
			WriteEpsfPreview(imageId);
	}
	else
		[super  endHeaderComments];

	return self;
}

/*
*	If saving in illustrator, override the prologue comment.
*/
- endPrologue
{
	DPSContext	ctxt;

	if (NXDrawingStatus == NX_COPYING)
	{
		ctxt = DPSGetCurrentContext();
		DPSPrintf(ctxt, "%%%%EndProlog\n\n");
	}
	else
		[super  endPrologue];

	return self;
}

/*  Initialize the Illustrator abbreviated proc set. */
- beginSetup
{
	DPSContext	ctxt;

	if (NXDrawingStatus == NX_COPYING)
	{
		ctxt = DPSGetCurrentContext();
		DPSPrintf(ctxt, "%%%%BeginSetup\n");
		WriteEpsfProcSetInit();
	}
	else
		[super  beginSetup];
		
	return self;
}

- endSetup
{
	DPSContext	ctxt;

	if (NXDrawingStatus == NX_COPYING)
	{
		ctxt = DPSGetCurrentContext();
		DPSPrintf(ctxt, "%%%%EndSetup\n");
	}
	else
		[super  endSetup];
		
	return self;
}

/*  Terminate the Illustrator abbreviated proc set. */
- beginTrailer
{
	DPSContext	ctxt;

	if (NXDrawingStatus == NX_COPYING)
	{
		ctxt = DPSGetCurrentContext();
		DPSPrintf(ctxt, "%%%%Trailer\n");
		WriteEpsfProcSetTerm();
	}
	else
		[super  beginTrailer];

	return self;
}

- endTrailer
{
	if (NXDrawingStatus != NX_COPYING)
		[super  endTrailer];

	return self;
}

@end
